#include "MCube.h"


MCube::MCube(float al, float ars,  float aox, float aoy, float aoz)
{
	name = "Cube";
	verts = NULL;
	points = NULL;
	normal = NULL;
	bNormal = false;
	updateCube(al,ars,aox,aoy,aoz);
}


MCube::~MCube(void)
{
	if(verts) {
		for(int i=0; i<seg; i++) {
			delete []verts[i];
		}
		delete []verts;
	}
	if(points) {
		delete []points;
	}
	if(normal) {
		delete []normal;
	}
}

MCube *MCube::makeCube(float al, float ars, float aox, float aoy, float aoz) {
	if(al<=0 || ars < 2 ) {//at least 1 radial stacks and 4 vertical slice
		return NULL;
	}
	return new MCube(al, ars,aox,aoy,aoz);
}

void MCube::updateCube(float al, float ars,  float aox, float aoy, float aoz) {
	if(al<=0 || ars < 2 ) {//at least 1 radial stacks and 4 vertical slice
		return;
	}
	//delete old array
	if(verts) {
		for(int i=0; i<seg; i++) {
			delete []verts[i];
		}
		delete []verts;
	}
	if(points) {
		delete []points;
	}
	if(normal) {
		delete []normal;
	}
	ox = aox;
	oy = aoy;
	oz = aoz;
	l = al;
	seg = ars;
	step = 1;
	//create array
	/*verts = new MVertex*[seg];
	for(i=0; i<seg; i++) {
		verts[i] = new MVertex[4*(seg-1)];
	}*/
	points = new MVertex[8];
	normal = new MVertex[8];
	//populate data
	/*verts[0][0] = MVertex(-l/2,l/2,l/2);
	for(j=1;j<=seg-1;j++) {
		verts[0][j] = verts[0][j-1].translate(l/(seg-1),0,0);
	}
	for(;j<=2*(seg-1);j++) {
		verts[0][j] = verts[0][j-1].translate(0,0,-l/(seg-1));
	}
	for(;j<=3*(seg-1);j++) {
		verts[0][j] = verts[0][j-1].translate(-l/(seg-1),0,0);
	}
	for(;j<4*(seg-1);j++) {
		verts[0][j] = verts[0][j-1].translate(0,0,l/(seg-1));
	}
	for(i=1;i<seg;i++) {
		for(j=0;j<4*(seg-1);j++) {
			verts[i][j] = verts[i-1][j].translate(0,-l/(seg-1),0);
		}
	}*/
	static const float hl = l/2;
	points[0] = MVertex(-hl,hl,hl);
	points[1] = MVertex(hl,hl,hl);
	points[2] = MVertex(hl,hl,-hl);
	points[3] = MVertex(-hl,hl,-hl);
	points[4] = MVertex(-hl,-hl,hl);
	points[5] = MVertex(hl,-hl,hl);
	points[6] = MVertex(hl,-hl,-hl);
	points[7] = MVertex(-hl,-hl,-hl);

	calculateNormal(&normal[0],points[0],points[3],points[4],points[3],points[1]);
	calculateNormal(&normal[1],points[1],points[0],points[5],points[0],points[2]);
	calculateNormal(&normal[2],points[2],points[1],points[6],points[1],points[3]);
	calculateNormal(&normal[3],points[3],points[2],points[7],points[2],points[0]);
	calculateNormal(&normal[4],points[4],points[5],points[0],points[5],points[7]);
	calculateNormal(&normal[5],points[5],points[6],points[1],points[6],points[4]);
	calculateNormal(&normal[6],points[6],points[7],points[2],points[7],points[5]);
	calculateNormal(&normal[7],points[7],points[4],points[3],points[4],points[6]);

	//update own axis
	v1 = MVertex(0,-hl,0);
	v2 = MVertex(0,hl,0);
}

void MCube::drawShape(){

}

void MCube::drawCube(void(*func)(const MVertex &p1,  const MVertex &p2, const MVertex &p3, int ic),
								void(*drawlin)(const MVertex &p1,  const MVertex &p2,int ic)) {
	if(!func || !verts) {
		return;
	}
	int icolor = 5;
	
	for(int i=0; i<seg-1;i++) {
		for(int j=0;j<4*(seg-1);j++) {
			int nextj = (j==4*seg-5?0:j+1);
			func(verts[i][j],verts[i+1][j],verts[i][nextj],icolor);
			func(verts[i+1][j],verts[i+1][nextj],verts[i][nextj],icolor);
		}
	}
}

void MCube::drawCube(void(*draw_poly)(const vector<MVertex> &v1, int ic),
							void(*drawline)(const MVertex &p1,  const MVertex &p2,int ic)) {
	if(!draw_poly || !points) {
		return;
	}
	int ic = 5;
	vector<MVertex> vp;
	vp.push_back(points[0]);vp.push_back(points[4]);
	vp.push_back(points[5]);vp.push_back(points[1]);
	draw_poly(vp,ic);

	vp.clear();
	vp.push_back(points[1]);vp.push_back(points[5]);
	vp.push_back(points[6]);vp.push_back(points[2]);
	draw_poly(vp,ic);

	vp.clear();
	vp.push_back(points[2]);vp.push_back(points[6]);
	vp.push_back(points[7]);vp.push_back(points[3]);
	draw_poly(vp,ic);

	vp.clear();
	vp.push_back(points[3]);vp.push_back(points[7]);
	vp.push_back(points[4]);vp.push_back(points[0]);
	draw_poly(vp,ic);

	vp.clear();
	vp.push_back(points[0]);vp.push_back(points[1]);
	vp.push_back(points[2]);vp.push_back(points[3]);
	draw_poly(vp,ic);

	vp.clear();
	vp.push_back(points[4]);vp.push_back(points[7]);
	vp.push_back(points[6]);vp.push_back(points[5]);
	draw_poly(vp,ic);

	if(bNormal && drawline) {
		for(int i = 0; i < 8; i++)
			drawline(points[i],points[i]+normal[i]*NORMALEN,1);
	}
}

void MCube::incrementSlice() {
	if(seg < 50) {
		this->updateCube(l,seg+1,ox,oy,oz);
	}
}

void MCube::decrementSlice() {
		if(seg > 2) {
		this->updateCube(l,seg-1,ox,oy,oz);
	}
}

void MCube::incrementStack() {
	if(seg < 50) {
		this->updateCube(l,seg+1,ox,oy,oz);
	}
}

void MCube::decrementStack() {
	if(seg > 2) {
		this->updateCube(l,seg-1,ox,oy,oz);
	}
}

void MCube::translate(float x, float y, float z) {
	if(points) {
		for(int i=0; i<8; i++) {
			points[i] = points[i].translate(x,y,z);
		}
		v1 = v1.translate(x,y,z);
		v2 = v2.translate(x,y,z);
	}
}

void MCube::rotate(float deg, int x, int y, int z) {
	float theta = deg/180*PI;
	if(x==1) {
		for(int i=0; i<8; i++) {
				points[i] = points[i].rotateX(theta);
				normal[i] = normal[i].rotateX(theta);
		}
		v1 = v1.rotateX(theta);
		v2 = v2.rotateX(theta);
	}
	if(y==1) {
		for(int i=0; i<8; i++) {
			points[i] = points[i].rotateY(theta);
			normal[i] = normal[i].rotateY(theta);
		}
		v1 = v1.rotateY(theta);
		v2 = v2.rotateY(theta);
	}
	if(z==1) {
		for(int i=0; i<8; i++) {
			points[i] = points[i].rotateZ(theta);
			normal[i] = normal[i].rotateZ(theta);
		}
		v1 = v1.rotateZ(theta);
		v2 = v2.rotateZ(theta);
	}
}

void MCube::scale(float x, float y, float z){
	if(points) {
		for(int i=0; i<8; i++) {
			points[i] = points[i].scaling(x,y,z);
		}
		v1 = v1.scaling(x,y,z);
		v2 = v2.scaling(x,y,z);
	}
}

void MCube::rotateAroundOwnAxis(float theta) {
	MMatrix mT, mM, mR, mMb, mTb;
	//set translate matrix
	mT.setMatrix(1,0,0,-v1.point[0],0,1,0,-v1.point[1],0,0,1,-v1.point[2],0,0,0,1);
	//set translate back matrix
	mTb.setMatrix(1,0,0,v1.point[0],0,1,0,v1.point[1],0,0,1,v1.point[2],0,0,0,1);
	//get its own world axis
	MVertex u = getVertex(v1,v2).normailize(); //u match to +x
	MVertex n,v;
	if(isEqual(u.point[0],0) && isEqual(u.point[2],0)) {//if u is the same as up vector
		if(isEqual(u.point[1],1)) {
			n = MVertex(1,0,0);
			v = MVertex(0,0,1);
		}
		else if(isEqual(u.point[1],-1)) {
			n = MVertex(0,0,1);
			v = MVertex(1,0,0);
		}
		else {
			fprintf(stderr,"rotateAroundOwnAxis error\n");
		}
	}
	else {
		n = u.CrossProduct(MVertex(0,1,0)).normailize(); //n match to +z
		v = n.CrossProduct(u).normailize(); //v match to +y
	}
	//set rotation matrix turn vertex from own axis to world axis
	mM.setMatrix(u.point[0], u.point[1], u.point[2], 0,
		v.point[0], v.point[1], v.point[2], 0,
		n.point[0], n.point[1], n.point[2],0,
		0,0,0,1);
	//set rotation matrix turn vertex from world axis to its own world
	mMb.setMatrix(u.point[0], v.point[0], n.point[0], 0,
		u.point[1], v.point[1], n.point[1], 0,
		u.point[2], v.point[2], n.point[2], 0,
		0,0,0,1);
	//set rotation matrix rotate vertex around x axis which is the central axis
	mR.setMatrix(1,0,0,0,
		0,cos(theta),-sin(theta),0,
		0,sin(theta),cos(theta),0,
		0,0,0,1);

	//now, get the final magic matrix
	//matrix = mTb * mMb * mR * mM * mT
	MMatrix normalRotation = mMb.dotMatrix(mR).dotMatrix(mM);
	matrix = mTb.dotMatrix(normalRotation).dotMatrix(mT);
	if(points && normal) {
		for(int i=0; i<8; i++) {
				points[i] = matrix.dotVertex(points[i]);
				normal[i] = normalRotation.dotVertex(normal[i]);
		}
	}
}